/*
 *  Copyright (c) 2018, Infineon Technologies AG
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification,are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holders nor the names of its contributors
 *  may be used to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  To improve the quality of the software, users are encouraged to share
 *  modifications, enhancements or bug fixes with Infineon Technologies AG
 *  dave@infineon.com).
 */
/**
 * \defgroup PWM Pulse Width Modulation
 * Control of peripherals for PWM generation.
 * @{
 */
/**
 * \file    config_boost_pwm.h
 * \author  Manuel Escudero Rodriguez
 * \date    09.05.2018
 * \brief   Pulse Width Modulation
 */
#ifndef __CONFIG_PWM_BOOST_H_
#define __CONFIG_PWM_BOOST_H_

#include "config_pwm.h"

/*---------------------------------------------------------------------------*/
/**
 * \brief       Shifts lagging leg PWM
 * \return      None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void boost_pwm_shift()
{
	static uint16_t phase_out_prchrg; /* Precharging time applied. [CCU_clk] */
	static uint16_t phase_out_inc_cnt; /* Filter of precharging time increment. [control_cyc] */
	static uint16_t phase_out_decr_cnt; /* Filter of precharging time decrement. [control_cyc] */
	static uint16_t phase_adjst_dly; /* Delay of precharging time change. [control_cyc] */
	static uint16_t phase_adjst_thrsld; /* Hysteresis window of precharging time adjustment. [CCU_clk] */
	uint16_t phase_out_new; /* Phase shift of primary bridge. [CCU_clk] */
	
	/* 
	 * Base precharging time is big enough not to increase precharging until
	 * load is high enough.
	 */
	if(current_ctr.adc_io_measure > BOOST_PRCHRG_THRSLD)
	{
		/* Not to do a division, we multiply by a decimal coefficient. */
		phase_out_new = ((current_ctr.adc_io_measure * BOOST_PRCHRG_LR_LOW) + ((current_ctr.adc_io_measure - BOOST_PRCHRG_THRSLD) * BOOST_PRCHRG_LR_HIGH)) >> PRCHRG_Q_DECIMALS;
	}
	else
	{
		/* Not to do a division, we multiply by a decimal coefficient. */
		phase_out_new = (current_ctr.adc_io_measure * BOOST_PRCHRG_LR_LOW) >> PRCHRG_Q_DECIMALS;
	}
	/* 
	 * During soft start more base precharging time to increase energy and avoid
	 * hard switching noise related issues.
	 */
	if(converter_ctr.status & SOFT)
	{
		phase_out_new = phase_out_new + BOOST_STRTP_XTRA_PRCHRG;
		phase_adjst_dly = BOOST_STRTP_PHASE_ADJST_DLY;
		phase_adjst_thrsld = BOOST_STRTP_PHASE_ADJST_THRSLD;
	}
	else
	{
		phase_adjst_dly = BOOST_PHASE_ADJST_DLY;
		phase_adjst_thrsld = BOOST_PHASE_ADJST_THRSLD;
	}

	/* 
	 * During trapping sequence reduce precharging time for reduced primary
	 * overshoot.
	 */
	if(converter_ctr.status & TRAPPING)
	{
		if(phase_out_prchrg > 0)
		{
			/* Progressive change of dead time. */
			phase_out_prchrg --;
		}
	}
	/* 
	 * Limits the rate of change of the dead time to avoid interactions 
	 * with the control loop. 
	 */
	else
	{
		if(phase_out_new > (phase_out_prchrg + phase_adjst_thrsld))
		{
			if(phase_out_inc_cnt > 0)
			{
				phase_out_inc_cnt --;
			}
			else
			{
				/* Progressive change of dead time. */
				phase_out_prchrg ++;
				/* Reset of the change filter. */
				phase_out_inc_cnt = phase_adjst_dly;
			}
			/* Reset of the change filter. */
			phase_out_decr_cnt = phase_adjst_dly;
		}
		else if((phase_out_new + phase_adjst_thrsld) < phase_out_prchrg)
		{
			if(phase_out_decr_cnt > 0)
			{
				phase_out_decr_cnt --;
			}
			else
			{
				/* Progressive change of dead time. */
				phase_out_prchrg --;
				/* Reset of the change filter. */
				phase_out_decr_cnt = phase_adjst_dly;
			}
			/* Reset of the change filter. */
			phase_out_inc_cnt = phase_adjst_dly;
		}
		else
		{
			/* Reset of the change filter. */
			phase_out_decr_cnt = phase_adjst_dly;
			phase_out_inc_cnt = phase_adjst_dly;
		}
	}
	
	phase_out_new = BOOST_PWM_PHASE_MAX - voltage_boost_ctr.duty_out + phase_out_prchrg;
	
	/* Lower saturation. */
	if(phase_out_new < BOOST_PHASE_MIN)
	{
		phase_out_new = BOOST_PHASE_MIN;
	}
	/* Upper saturation. */
	else if(phase_out_new > MAX_ADJ_PHASE)
	{
		phase_out_new = MAX_ADJ_PHASE;
	}
    /*
     * Comparison value 2 of the PWM_A_B triggers the stop event of the slice PWM_D.
     * Stop of PWM_D triggers start of PWM_C. This way the shift is adjusted by PWM_A_B means.
     * NOTE: CR2S sets steps of 12.5ns.
     */
    PWM_A_B.ccu8_slice_ptr->CR2S = phase_out_new;
    /** \todo Check if is needed to call shadow transfer of PWM_C and PWM_D. */
    PWM_A_B.ccu8_module_ptr->GCSS = PWM_A_B.shadow_txfr_msk | PWM_C.shadow_txfr_msk | PWM_D.shadow_txfr_msk;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief       Synchronous DCM mode
 * \return      None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void boost_pwm_sync_cfg()
{
	int16_t cr_value; /* New calculated duty. [CCU_clk] */
	
	/* Deactivation of starting event before the reconfiguration. */
	PWM_E.ccu4_slice_ptr->CMC &= ~((uint32_t) CCU4_CC4_CMC_STRTS_Msk);
	PWM_F.ccu4_slice_ptr->CMC &= ~((uint32_t) CCU4_CC4_CMC_STRTS_Msk);
	/* Clear and stop timers. */
	PWM_E.ccu4_slice_ptr->TCCLR = ((uint32_t) CCU4_CC4_TCCLR_TRBC_Msk | (uint32_t) CCU4_CC4_TCCLR_TCC_Msk);
	PWM_F.ccu4_slice_ptr->TCCLR = ((uint32_t) CCU4_CC4_TCCLR_TRBC_Msk | (uint32_t) CCU4_CC4_TCCLR_TCC_Msk);

	cr_value = BOOST_PWM_DTY_ABS_MAX - voltage_boost_ctr.duty_out;
	/*
	 * Even if the duty cycle is not updated on decreasing phase shift, the current adjustment should be
	 * updated in prevision of sudden load decrease which may cause the gate signal to overlap with the VDS voltage.
	 */
	/* Update new values into PWM. */
	PWM_E.ccu4_slice_ptr->CRS = cr_value;
	PWM_E.ccu4_slice_ptr->PRS = BOOST_SYNC_MAX_PERIOD - BOOST_SYNC_STRP_OVRLP;
	/* Adaptive turn OFF delay of synchronous in buck mode. */
	PWM_STOP_E.ccu4_slice_ptr->CRS = 0;
	PWM_STOP_E.ccu4_slice_ptr->PRS = 2;
	
	PWM_F.ccu4_slice_ptr->CRS = cr_value;
	/* Small period deviation to compensate for turn ON interconnection delay. */
	PWM_F.ccu4_slice_ptr->PRS = BOOST_SYNC_MAX_PERIOD - BOOST_SYNC_STRP_OVRLP;
	/* Adaptive turn OFF delay of synchronous in buck mode. */
	PWM_STOP_F.ccu4_slice_ptr->CRS = 0;
	PWM_STOP_F.ccu4_slice_ptr->PRS = 1;
	
    /* Triggers shadow transfer of all synch related timers. */
    PWM_F.ccu4_module_ptr->GCSS = PWM_E.shadow_txfr_msk | PWM_F.shadow_txfr_msk | PWM_STOP_E.shadow_txfr_msk | PWM_STOP_F.shadow_txfr_msk;
	
	/* 
	 * Activates stopping events in E and F timer slices.
	 * Event 1 from PWM_STOP_F (B falling edge) for PWM_F and PWM_STOP_E (A falling edge) for PWM_E. 
	 */
	PWM_E.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV1IS_Pos));
	PWM_F.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV1IS_Pos));
	/* Input M -> PWM_STOP_E */
	PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_M) << ((uint8_t) CCU4_CC4_INS_EV1IS_Pos);
	/* Input O -> PWM_STOP_F */
	PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_O) << ((uint8_t) CCU4_CC4_INS_EV1IS_Pos);
	PWM_E.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV1EM_Msk);
	PWM_F.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV1EM_Msk);
	PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE << (uint32_t) CCU4_CC4_INS_EV1EM_Pos);
	PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE << (uint32_t) CCU4_CC4_INS_EV1EM_Pos);
	PWM_E.ccu4_slice_ptr->CMC &= ~((uint32_t) CCU4_CC4_CMC_ENDS_Msk);
	PWM_F.ccu4_slice_ptr->CMC &= ~((uint32_t) CCU4_CC4_CMC_ENDS_Msk);
	PWM_E.ccu4_slice_ptr->CMC |= ((uint32_t) XMC_CCU4_SLICE_EVENT_1 << (uint32_t) CCU4_CC4_CMC_ENDS_Pos);
	PWM_F.ccu4_slice_ptr->CMC |= ((uint32_t) XMC_CCU4_SLICE_EVENT_1 << (uint32_t) CCU4_CC4_CMC_ENDS_Pos);
	
	/* 
	 * Activates starting events in E and F timer slices. 
	 * Event 1 from PWM_STOP_F (B falling edge) for PWM_E and PWM_STOP_E (A falling edge) for PWM_F.
	 */
	PWM_E.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos));
	PWM_F.ccu4_slice_ptr->INS &= ~(((uint32_t) XMC_CCU4_SLICE_EVENT_INPUT_CONFIG_MASK) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos));
	/* Input M -> PWM_STOP_E */
	PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_M) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos);
	/* Input O -> PWM_STOP_F */
	PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_INPUT_O) << ((uint8_t) CCU4_CC4_INS_EV0IS_Pos);
	PWM_E.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV0EM_Msk);
	PWM_F.ccu4_slice_ptr->INS &= ~((uint32_t) CCU4_CC4_INS_EV0EM_Msk);
	PWM_E.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_FALLING_EDGE << (uint32_t) CCU4_CC4_INS_EV0EM_Pos);
	PWM_F.ccu4_slice_ptr->INS |= ((uint32_t) XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_FALLING_EDGE << (uint32_t) CCU4_CC4_INS_EV0EM_Pos);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief       Adjust synchronous rectification PWM duty cycle.
 * \return      None
 *
 * There is one mode of synchronous rectification in peak control:
 * 	-	During power transfer one of the synchronous rectifiers is maintained ON while the opposite is switched OFF.
 * 		During the overlap of PWM_A and PWM_D -> PWM_E would be switched OFF.
 * 		During the overlap of PWM_B and PWM_C -> PWM_F would be switched OFF.
 * 	The first mode is the used for medium and high loads.
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void boost_pwm_sync_shift()
{
	int16_t cr_value; /* New turn on delay calculated. [CCU_clk] */
	int16_t pr_value; /* New period calculated. [CCU_clk] */

	/* Note: voltage_boost_ctr is volatile. */
	cr_value = BOOST_PWM_DTY_ABS_MAX - voltage_boost_ctr.duty_out - voltage_boost_ctr.sync_strp_ovrlp;
	pr_value = BOOST_SYNC_MAX_PERIOD - voltage_boost_ctr.sync_strp_ovrlp;

duty_test = voltage_boost_ctr.duty_out;
if(duty_test_max < voltage_boost_ctr.duty_out)
	duty_test_max = voltage_boost_ctr.duty_out;   
cr_value_test = cr_value;

	/* Update new values into PWM. */
	PWM_E.ccu4_slice_ptr->CRS = cr_value;
	PWM_E.ccu4_slice_ptr->PRS = pr_value;
	PWM_F.ccu4_slice_ptr->CRS = cr_value;
	/* Slight delay of internal controller connections compensation. */
	PWM_F.ccu4_slice_ptr->PRS = pr_value;
    /* Triggers shadow transfer of all synch related timers. */
    PWM_F.ccu4_module_ptr->GCSS = PWM_E.shadow_txfr_msk | PWM_F.shadow_txfr_msk;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief       Starts synchronous rectifying
 * \return      None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void boost_pwm_sync_start()
{
    /* If the synchronous rectifier had been annulled */
	if(!(PWM_E.ccu4_slice_ptr->CMC & ((uint32_t) CCU4_CC4_CMC_STRTS_Msk)))
    {
		PWM_E.ccu4_slice_ptr->CMC |= ((uint32_t) XMC_CCU4_SLICE_EVENT_0 << (uint32_t) CCU4_CC4_CMC_STRTS_Pos);
		PWM_F.ccu4_slice_ptr->CMC |= ((uint32_t) XMC_CCU4_SLICE_EVENT_0 << (uint32_t) CCU4_CC4_CMC_STRTS_Pos);
    }
}
/*---------------------------------------------------------------------------*/
/**
 * \brief       Reconfigures PWM into boost mode back from buck mode.
 * \return      None
 */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
static inline void buck_to_boost_pwm_cfg()
{
    pwm_blk_set(CSG_BLK);
    peak_ref_set(PWM_PEAK_REF_MAX);
	boost_pwm_sync_cfg();
	/* In case buck mode was using burst. Burst counter is not used in boost mode. */
	if(BURST_CNT.ccu4_handle->slice_ptr->TCST & CCU4_CC4_TCST_TRB_Msk)
	{
		/* Stops and clears burst timer. */
		BURST_CNT.ccu4_handle->slice_ptr->TCCLR = ((uint32_t) CCU4_CC4_TCCLR_TRBC_Msk);
	}
}
#endif /* __CONFIG_PWM_H_ */
/** @} */
